
import java.rmi.RemoteException;
import java.util.*;
import java.awt.Point;

public class PlayerData implements PlayerListener {
	
	private CardSet heldCards = new CardSet();	//cards controlled by selector
	private CardSet selectedCards = new CardSet();	//selected cards: may or may not contain some or all of held cards
	private PlayerModel player;
	
	//put this in table panel when lock is available
	private static int snapX = 18;
	private static int snapY = 24;
	private static int snapRatio = 4;
	private boolean snap = true;
	
	
	public PlayerData(PlayerModel p){
		player = p;
		player.alert();
	}
	
	public PlayerData() {
		
	}
	
/**
	The <code>Player</code> takes control the selected cards.
	*/
	public void hold() {
		ArrayList<Card> cardsLeftToPlace = new ArrayList<Card>(selectedCards);
		ZComparator.sort(cardsLeftToPlace);
		while (!cardsLeftToPlace.isEmpty()) {
			Card c = cardsLeftToPlace.remove(0);
			//fix z
			c.setZ(findHighestZ(c,cardsLeftToPlace) + 1);
		}
		player.alert();
	
	}
	
	/**
	The <code>Player</code> relinquishes control of the selected cards.
	*/
	public void release() {
		
		for(Card card: selectedCards) {
			int x = card.getLocation().x;
			int y = card.getLocation().y;
			
			if(snap == true) {
				x = snapX*Math.round((float)x/(float)snapX);
				y = snapY*Math.round((float)y/(float)snapY);
			}
			card.setLocation(x, y);
		}
		
		player.alert();
		player.sendInfoAcrossServer();
		
	}
	
	public void setSnap(boolean s) {
		snap = s;
	}
	
	//////// the following methods should only be called when player does not have control.
	
	public void select(Set<Card> cards) {
		
		selectedCards = (CardSet)cards;
		Dbg.println("SELECT");
		
		if(cards.isEmpty()) {
			player.sendDeselect();
		} else {
			player.sendSelect(getSelectedCardIds());
		}
		
	}
	
	
	//////// the following methods should only be called when player has control.
	
	/**
	The player drops some of the cards they are currently controlling.
	*/
	public void dropSelection(Set<Card> cards) {
		heldCards.removeAll(cards);
	}
	
	/**
	The <code>Player</code> moves the selected cards.  Must be preceded by a call to <code>pickUp</code>.
	@param point the amount to offset the cards by.
	*/
	public boolean moveBy(Point point){
		int dx = point.x;
		int dy = point.y;
		boolean hugesuccess = true;
		
		ArrayList<Card> cardsLeftToPlace = new ArrayList<Card>(selectedCards);
		ZComparator.sort(cardsLeftToPlace);
		while (!cardsLeftToPlace.isEmpty()) {
			Card c = cardsLeftToPlace.remove(0);
			Point oldLoc = c.getLocation();
			hugesuccess = hugesuccess && c.setLocation(new Point(oldLoc.x + dx, oldLoc.y + dy));
			//fix z
			c.setZ(findHighestZ(c,cardsLeftToPlace) + 1);
		}
		
		player.sendMoveBy(point);
		player.alert();
		return hugesuccess;
	}
	
	public void flip() {
		System.out.println("Flip - " + selectedCards.toString());
		
		if (selectedCards.isEmpty()) {
			return;
		}
		for (Card c: selectedCards) {
			c.toggleFaceUp();
		}
		player.alert();
	}
	
	public Table getTable() {
		return getRoom().getTable();
	}
	
	public boolean isSelectedCard(Card card) {
		return selectedCards.contains(card);
	}
	
	public int findHighestZ(Card selected) {
		return findHighestZ(selected, new ArrayList<Card>());
	}
	
	/**
	 * Calculates the highest Z-index of all cards intersecting with a given {@link Card}.
	 * 
	 * @param selected The selected <code>Card</code>.
	 * @param except do not count these cards' z value, when finding the highest value.  <code>selected</code> will be automatically considered to be in this group.
	 * @return The highest Z-index found.
	 */
	private int findHighestZ(Card selected, List<Card> except) {
		
		CardSet cards = getRoom().getTable().getCards();
		int highestZ = 0;
	
		for(Card card: cards){
			
			if(except.contains(card)) continue;
			if(selected == card) continue;
			
			if(selected.getRectangle().intersects(card.getRectangle())){//intersects with point
				if(card.getZ() > highestZ){//check if highest z
					highestZ = card.getZ();//set c and z
				}
			}
		}
		
		//System.out.println("z " + (highestZ + 1));
		return highestZ;
	}
	
	private Room getRoom() {
		return player.getModel().getRoom();
	}
	
	public int[] getHeldCardIds() {
		return heldCards.getCardIDs();
	}

	/**
	 * @return the selectedCard Ids
	 */
	public int[] getSelectedCardIds() {
		return selectedCards.getCardIDs();
	}

	public void moveBy(int x, int y) {
		moveBy(new Point(x,y));
	}
	
	public void shuffle(){
		selectedCards.shuffle();
		//updateServer();
	}
	
	public void setPlayerModel(PlayerModel pm) {
		this.player = pm;
	}
	
	public void sendToBack() {
// 		int numcards = selectedCards.size();
		for (Card c: selectedCards) {
			c.setZ(0);
		}
		player.alert();
		player.sendInfoAcrossServer();
	}
	
	
	// set permissions on cards
	//public void setViewPermissions...

//rememba set of cards
//copy sum out of model.java
}
